﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Markup;
using Red.Controls.Utils;
using Red.Core;

namespace Red.Controls.Shell
{
    internal static class PageSwitcherCommands
    {
        static public RedCommand CloseTabCommand = new RedCommand( "Close tab", "appbar_close" );
    }

    public partial class PageSwitcherControl : UserControl
    {
        public PageSwitcher Owner { get; set; }

        public PageSwitcherControl()
        {
            InitializeComponent();
        }

        private void TabHeader_PreviewMouseDown( object sender, MouseButtonEventArgs e )
        {
            if ( e.ChangedButton == MouseButton.Middle && e.ButtonState == MouseButtonState.Pressed )
            {
                if ( Owner.ShowCloseButtons )
                {
                    var tabItem = Helpers.FindVisualAncestorOfType<TabItem>( e.OriginalSource );
                    PageSwitcherCommands.CloseTabCommand.Execute( (Page)tabItem.Content, this );
                }
            }
        }
    }

    /// <summary>
    ///     Collection of switchable pages
    /// </summary>
    [ContentProperty( "Content" )]
    public class PageSwitcher : ElementFacade<PageSwitcherControl>
    {
        #region Properties
        
        public new static readonly DependencyProperty ContentProperty =
            DependencyProperty.Register( "Content", typeof( ObservableCollection<Page> ), typeof( PageSwitcher ) );

        public static readonly DependencyProperty DockProperty = // It's Nullable to force the first set trigger OnChanged event
            DependencyProperty.Register( "Dock", typeof( Dock? ), typeof( PageSwitcher ), new FrameworkPropertyMetadata( DockPropertyChanged ) );

        private static readonly DependencyPropertyKey OrientationPropertyKey =
            DependencyProperty.RegisterReadOnly( "Orientation", typeof( Orientation? ), typeof( PageSwitcher ), new FrameworkPropertyMetadata( null ) );

        public static readonly DependencyProperty OrientationProperty = OrientationPropertyKey.DependencyProperty;

        public static readonly DependencyProperty ShowCloseButtonsProperty =
            DependencyProperty.Register( "ShowCloseButtons", typeof( bool ), typeof( PageSwitcher ) );

        public static readonly DependencyProperty HideLastTabProperty =
            DependencyProperty.Register( "HideLastTab", typeof( bool ), typeof( PageSwitcher ) );
                
        /// <summary>
        ///     The content of PageSwitcher - collection of pages
        /// </summary>
        public new ObservableCollection<Page> Content
        {
            get { return (ObservableCollection<Page>)GetValue( ContentProperty ); }
            set { SetValue( ContentProperty, value ); }
        }

        /// <summary>
        ///     Where to dock the PageSwitcher. Supported values are Left, Right and Bottom. Describes the 'desired' home for
        ///     the drawer, so keeps its value even if the drawer is floating
        /// </summary>
        public Dock Dock
        {
            get { return (Dock)GetValue( DockProperty ); }
            set { SetValue( DockProperty, value ); }
        }

        /// <summary>
        ///     Read only property that describes if the PageSwitcher is Horizontal (Top or Bottom) or Vertical (Left or Right)
        /// </summary>
        public Orientation Orientation
        {
            get { return (Orientation)GetValue( OrientationProperty ); }
            private set { SetValue( OrientationPropertyKey, value ); }
        }

        /// <summary>
        ///     Whether or not the page headers show 'x' button to close them.
        /// </summary>
        public bool ShowCloseButtons
        {
            get { return (bool)GetValue( ShowCloseButtonsProperty ); }
            set { SetValue( ShowCloseButtonsProperty, value ); }
        }

        /// <summary>
        ///     Gets or sets the active (visible) page.
        /// </summary>
        /// <exception cref="ArgumentException">
        ///     Invalid page, or page does not exist in drawer
        /// </exception>
        public Page ActivePage
        {
            get
            {
                var item = Element._tabs.SelectedItem as TabItem;
                return item == null ? null : item.Content as Page;
            }

            set
            {
                foreach ( TabItem item in Element._tabs.Items )
                {
                    if ( item.Content == value )
                    {
                        Element._tabs.SelectedItem = item;
                        return;
                    }
                }

                throw new ArgumentException( "Invalid page, or page does not exist in drawer" );
            }
        }

        /// <summary>
        ///     Gets or sets the active (visible) page index
        /// </summary>
        public int ActivePageIndex
        {
            get { return Element._tabs.SelectedIndex; }
            set { Element._tabs.SelectedIndex = value; }
        }

        /// <summary>
        ///     When set to true, the tab bar will be hidden when there is only one page
        /// </summary>
        public bool HideLastTab
        {
            get { return (bool)GetValue( HideLastTabProperty ); }
            set { SetValue( HideLastTabProperty, value ); }
        }

        #endregion Properties

        #region Events

        public static readonly RoutedEvent ActivePageChangedEvent = EventManager.RegisterRoutedEvent( "ActivePageChanged", RoutingStrategy.Bubble, typeof( RoutedEventHandler ), typeof( PageSwitcher ) );

        public event RoutedEventHandler ActivePageChanged
        {
            add { AddHandler( ActivePageChangedEvent, value ); }
            remove { RemoveHandler( ActivePageChangedEvent, value ); }
        }

        private void OnActivePageChanged()
        {
            var eventArgs = new RoutedEventArgs( ActivePageChangedEvent, this );
            RaiseEvent( eventArgs );
        }

        #endregion Events

        #region C-tor

        public PageSwitcher()
        {
            Content = new ObservableCollection<Page>(); // WARNING: This CANNOT be put as a property default value
            Content.CollectionChanged += Pages_CollectionChanged;
            Element._tabs.SelectionChanged += Tabs_SelectionChanged;
            Element.Owner = this;
            CommandBindings.Add( new CommandBinding( PageSwitcherCommands.CloseTabCommand, CloseTab_Executed ) );
        }

        #endregion C-tor


        #region Private methods

        private void AddPageTab( Page page )
        {
            var item = new TabItem { Content = page };
            item.SetBinding( TabItem.HeaderProperty, new Binding( "Title" ) { Source = page } );
            page.IsListedChanged += Page_IsListedChanged;
            Element._tabs.Items.Add( item );
        }

        private void RemovePageTab( Page page )
        {
            page.IsListedChanged -= Page_IsListedChanged;
            foreach ( TabItem item in Element._tabs.Items )
            {
                if ( item.Content == page )
                {
                    Element._tabs.Items.Remove( item );
                    return;
                }
            }
        }

        private void UpdateBarVisibility()
        {
            if ( HideLastTab && Element._tabs.Items.Count != 0 )
            {
                var items = Element._tabs.Items;
                ((TabItem)items[0]).Visibility = ( items.Count == 1 ) ? Visibility.Collapsed : Visibility.Visible;
            }
        }

        #endregion Private methods

        #region Event handlers

        private void Tabs_SelectionChanged( object sender, SelectionChangedEventArgs e )
        {
            // We need to check this because SelectionChanged is called even when we don't touch tabs.
            // It also gets fired when we click through contents of one tab.
            if ( e.OriginalSource == Element._tabs )
            {
                OnActivePageChanged();
            }
        }

        private void CloseTab_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            var page = e.Parameter as Page;
            Content.Remove( page );
        }

        private void Pages_CollectionChanged( object sender, NotifyCollectionChangedEventArgs e )
        {
            switch ( e.Action )
            {
                case NotifyCollectionChangedAction.Add:
                    foreach ( object newItem in e.NewItems )
                    {
                        var page = newItem as Page;
                        AddPageTab( page );
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach ( var item in e.OldItems )
                    {
                        var page = item as Page;
                        RemovePageTab( page );
                    }
                    break;
            }

            UpdateBarVisibility();
        }

        private void Page_IsListedChanged( object sender, EventArgs e )
        {
            // rebuild pages
            Element._tabs.Items.Clear();
            foreach ( Page page in Content )
            {
                if ( page.IsListed )
                {
                    AddPageTab( page );
                }
            }

            UpdateBarVisibility();
        }

        private static void DockPropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
        {
            var switcher = (PageSwitcher)d;
            var dock = (Dock)e.NewValue;
            switcher.Orientation = ( dock == Dock.Left || dock == Dock.Right ) ? Orientation.Vertical : Orientation.Horizontal;
        }
        
        #endregion Event handlers
    }
}
